import os
import tkinter as tk
from tkinter import filedialog, scrolledtext, messagebox
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from elftools.elf.elffile import ELFFile
import traceback
from io import BytesIO

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View


@annotate(ClassInfo(name="LibParser", category="dev_tool"))
class LibParserView(View):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 当前文件路径和解析结果
        self.current_file = None
        self.parse_results = []
        self.full_parse = False  # 是否显示完整内容
        self.search_matches = []  # 搜索匹配位置
        self.current_match = -1  # 当前匹配索引

        # 创建界面
        self._create_widgets()

    def _create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self, padding=10)
        main_frame.pack(fill=BOTH, expand=YES)

        # 顶部：文件选择和操作按钮
        top_frame = ttk.Frame(main_frame, padding=5)
        top_frame.pack(fill=X, pady=(0, 10))

        # 文件路径显示
        self.file_path_var = tk.StringVar()
        self.file_entry = ttk.Entry(top_frame, textvariable=self.file_path_var, state=DISABLED)
        self.file_entry.pack(side=LEFT, fill=X, expand=YES, padx=(0, 5))

        # 按钮区域
        btn_frame = ttk.Frame(top_frame)
        btn_frame.pack(side=RIGHT)

        # 按钮样式设置
        btn_style = ttk.Style()
        btn_style.configure("Toolbutton.TButton", padding=5)

        self.open_btn = ttk.Button(
            btn_frame,
            text="打开并解析",
            command=self.open_and_parse,
            bootstyle=SUCCESS,
            style="Toolbutton.TButton"
        )
        self.open_btn.pack(side=LEFT, padx=2)

        self.reparse_btn = ttk.Button(
            btn_frame,
            text="重新解析",
            command=self.reparse,
            bootstyle=INFO,
            state=DISABLED,
            style="Toolbutton.TButton"
        )
        self.reparse_btn.pack(side=LEFT, padx=2)

        self.full_display_btn = ttk.Checkbutton(
            btn_frame,
            text="显示全部内容",
            command=self.toggle_full_display,
            bootstyle=INFO
        )
        self.full_display_btn.pack(side=LEFT, padx=2)

        self.save_btn = ttk.Button(
            btn_frame,
            text="保存记录",
            command=self.save_results,
            bootstyle=PRIMARY,
            state=DISABLED,
            style="Toolbutton.TButton"
        )
        self.save_btn.pack(side=LEFT, padx=2)

        self.close_btn = ttk.Button(
            btn_frame,
            text="关闭",
            command=self.close_file,
            bootstyle=DANGER,
            state=DISABLED,
            style="Toolbutton.TButton"
        )
        self.close_btn.pack(side=LEFT, padx=2)

        # # 折叠/展开按钮
        # fold_frame = ttk.Frame(main_frame, padding=5)
        # fold_frame.pack(fill=X, pady=(0, 5))
        #
        # self.fold_all_btn = ttk.Button(
        #     fold_frame,
        #     text="全部折叠",
        #     command=self.fold_all,
        #     bootstyle=SECONDARY,
        #     state=DISABLED
        # )
        # self.fold_all_btn.pack(side=LEFT, padx=2)
        #
        # self.unfold_all_btn = ttk.Button(
        #     fold_frame,
        #     text="全部展开",
        #     command=self.unfold_all,
        #     bootstyle=SECONDARY,
        #     state=DISABLED
        # )
        # self.unfold_all_btn.pack(side=LEFT, padx=2)

        # 搜索区域
        search_frame = ttk.Frame(main_frame, padding=5)
        search_frame.pack(fill=X, pady=(0, 10))

        self.search_var = tk.StringVar()
        self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=30)
        self.search_entry.pack(side=LEFT, padx=(0, 5))
        self.search_entry.bind('<Return>', lambda e: self.search_text())

        self.search_btn = ttk.Button(
            search_frame,
            text="搜索",
            command=self.search_text,
            bootstyle=PRIMARY
        )
        self.search_btn.pack(side=LEFT, padx=2)

        self.prev_btn = ttk.Button(
            search_frame,
            text="上一个",
            command=self.prev_match,
            bootstyle=SECONDARY,
            state=DISABLED
        )
        self.prev_btn.pack(side=LEFT, padx=2)

        self.next_btn = ttk.Button(
            search_frame,
            text="下一个",
            command=self.next_match,
            bootstyle=SECONDARY,
            state=DISABLED
        )
        self.next_btn.pack(side=LEFT, padx=2)

        self.match_count_var = tk.StringVar(value="找到 0 个匹配")
        self.match_count_label = ttk.Label(search_frame, textvariable=self.match_count_var)
        self.match_count_label.pack(side=LEFT, padx=10)

        # 中间：结果展示区域（带行号）
        result_frame = ttk.LabelFrame(main_frame, text="解析结果", padding=5)
        result_frame.pack(fill=BOTH, expand=YES, pady=(0, 10))

        # 行号区域
        self.line_numbers = tk.Text(result_frame, width=5, padx=3, takefocus=0, border=0,
                                    background='#f0f0f0', state=tk.DISABLED, wrap=tk.NONE)
        self.line_numbers.pack(side=tk.LEFT, fill=tk.Y)

        # 结果文本区域
        self.result_text = scrolledtext.ScrolledText(
            result_frame,
            wrap=tk.WORD,
            font=('Consolas', 10),
            bg="#f8f9fa",
            relief=FLAT
        )
        self.result_text.pack(fill=BOTH, expand=YES, padx=(0, 2), pady=2)
        self.result_text.config(state=DISABLED)

        # 绑定滚动事件，使行号与内容同步
        self.result_text.bind('<Configure>', self.update_line_numbers)
        self.result_text.bind('<MouseWheel>', self.update_line_numbers)
        self.result_text.bind('<Button-4>', self.update_line_numbers)  # Linux 滚轮上
        self.result_text.bind('<Button-5>', self.update_line_numbers)  # Linux 滚轮下

        # 绑定双击事件用于折叠/展开
        self.result_text.bind('<Double-1>', self.toggle_fold)

        # 底部：状态信息
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(
            self,
            textvariable=self.status_var,
            relief=SUNKEN,
            anchor=W,
            padding=(5, 2)
        )
        status_bar.pack(side=BOTTOM, fill=X)

        # 存储折叠状态的字典：行号 -> 是否折叠
        self.folded_lines = {}
        # 存储每行的缩进级别
        self.line_indent_levels = []

    def update_line_numbers(self, event=None):
        """更新行号显示"""
        # 获取文本内容的行数
        line_count = int(self.result_text.index('end-1c').split('.')[0])

        # 更新行号文本
        self.line_numbers.config(state=tk.NORMAL)
        self.line_numbers.delete(1.0, tk.END)

        # 添加行号，折叠的行显示特殊标记
        for i in range(1, line_count + 1):
            if i in self.folded_lines and self.folded_lines[i]:
                self.line_numbers.insert(tk.END, f"> {i}\n")  # 折叠行标记
            else:
                self.line_numbers.insert(tk.END, f"{i}\n")

        self.line_numbers.config(state=tk.DISABLED)

        # 保持滚动位置同步
        self.line_numbers.yview_moveto(self.result_text.yview()[0])

    def toggle_full_display(self):
        """切换是否显示完整内容"""
        self.full_parse = not self.full_parse
        if self.current_file:
            self.parse_lib_file()

    def search_text(self):
        """搜索文本并高亮显示匹配项"""
        search_term = self.search_var.get()
        if not search_term:
            self.clear_search()
            return

        # 清除之前的高亮
        self.clear_search_highlight()

        # 在文本中查找所有匹配
        self.search_matches = []
        start_pos = "1.0"

        self.result_text.tag_remove("search", "1.0", tk.END)
        self.result_text.tag_config("search", background="#ffff99")

        while True:
            start_pos = self.result_text.search(search_term, start_pos, stopindex=tk.END)
            if not start_pos:
                break

            # 计算结束位置
            line, col = map(int, start_pos.split('.'))
            end_pos = f"{line}.{col + len(search_term)}"

            self.search_matches.append((start_pos, end_pos))
            self.result_text.tag_add("search", start_pos, end_pos)

            start_pos = end_pos

        # 更新匹配计数
        self.current_match = -1
        self.update_match_count()

        # 启用/禁用搜索按钮
        if self.search_matches:
            self.prev_btn.config(state=NORMAL)
            self.next_btn.config(state=NORMAL)
            # 自动跳转到第一个匹配
            self.current_match = 0
            self.highlight_current_match()
        else:
            self.prev_btn.config(state=DISABLED)
            self.next_btn.config(state=DISABLED)

    def clear_search(self):
        """清除搜索状态"""
        self.clear_search_highlight()
        self.search_matches = []
        self.current_match = -1
        self.update_match_count()
        self.prev_btn.config(state=DISABLED)
        self.next_btn.config(state=DISABLED)

    def clear_search_highlight(self):
        """清除搜索高亮"""
        self.result_text.tag_remove("search", "1.0", tk.END)
        self.result_text.tag_remove("current_search", "1.0", tk.END)

    def update_match_count(self):
        """更新匹配计数显示"""
        total = len(self.search_matches)
        if total == 0:
            self.match_count_var.set("找到 0 个匹配")
        else:
            current = self.current_match + 1 if self.current_match >= 0 else 0
            self.match_count_var.set(f"找到 {total} 个匹配，当前 {current}/{total}")

    def prev_match(self):
        """跳转到上一个匹配"""
        if not self.search_matches:
            return

        self.current_match = (self.current_match - 1) % len(self.search_matches)
        self.highlight_current_match()

    def next_match(self):
        """跳转到下一个匹配"""
        if not self.search_matches:
            return

        self.current_match = (self.current_match + 1) % len(self.search_matches)
        self.highlight_current_match()

    def highlight_current_match(self):
        """高亮显示当前匹配并滚动到视图"""
        if not self.search_matches or self.current_match < 0:
            return

        # 移除之前的当前匹配高亮
        self.result_text.tag_remove("current_search", "1.0", tk.END)
        self.result_text.tag_config("current_search", background="#ffcc99")

        # 高亮当前匹配
        start, end = self.search_matches[self.current_match]
        self.result_text.tag_add("current_search", start, end)

        # 滚动到当前匹配
        self.result_text.see(start)

        # 更新计数显示
        self.update_match_count()

    def fold_all(self):
        """折叠所有可折叠内容"""
        if not self.parse_results:
            return

        # 重置折叠状态
        self.folded_lines = {}

        # 折叠所有有子级的行
        for i in range(len(self.line_indent_levels)):
            level = self.line_indent_levels[i]
            # 查找有子级的行（后面有更高缩进级别的行）
            for j in range(i + 1, len(self.line_indent_levels)):
                if self.line_indent_levels[j] > level:
                    self.folded_lines[i + 1] = True  # 行号从1开始
                    break

        self.update_display_with_folding()

    def unfold_all(self):
        """展开所有内容"""
        self.folded_lines = {}
        self.update_display_with_folding()

    def toggle_fold(self, event):
        """切换折叠/展开状态"""
        # 获取双击位置的行号
        line = self.result_text.index(f"@{event.x},{event.y}").split('.')[0]
        line_num = int(line)

        # 检查该行是否可以折叠
        if line_num - 1 >= len(self.line_indent_levels):
            return

        current_level = self.line_indent_levels[line_num - 1]

        # 检查是否有子级可以折叠/展开
        has_children = False
        for i in range(line_num, len(self.line_indent_levels)):
            if self.line_indent_levels[i] > current_level:
                has_children = True
                break

        if not has_children:
            return

        # 切换折叠状态
        if line_num in self.folded_lines and self.folded_lines[line_num]:
            self.folded_lines[line_num] = False
        else:
            self.folded_lines[line_num] = True

        self.update_display_with_folding()

    def update_display_with_folding(self):
        """根据折叠状态更新显示内容"""
        if not self.parse_results:
            return

        # 构建要显示的内容（考虑折叠状态）
        display_lines = []
        skip_until_level = -1

        for i in range(len(self.parse_results)):
            line_num = i + 1  # 行号从1开始
            level = self.line_indent_levels[i]

            # 检查是否需要跳过此行
            if skip_until_level != -1:
                if level <= skip_until_level:
                    skip_until_level = -1  # 可以停止跳过
                else:
                    continue  # 继续跳过

            # 检查当前行是否被折叠
            if line_num in self.folded_lines and self.folded_lines[line_num]:
                display_lines.append(self.parse_results[i] + "  [折叠...]")
                skip_until_level = level  # 跳过所有更高缩进级别的行
            else:
                display_lines.append(self.parse_results[i])

        # 更新显示
        self.result_text.config(state=NORMAL)
        self.result_text.delete(1.0, END)
        self.result_text.insert(END, "\n".join(display_lines))
        self.result_text.config(state=DISABLED)

        # 重新应用搜索高亮
        self.reapply_search_highlight()

        # 更新行号
        self.update_line_numbers()

    def reapply_search_highlight(self):
        """重新应用搜索高亮（折叠后可能需要重新高亮可见内容）"""
        if not self.search_matches:
            return

        self.result_text.tag_remove("search", "1.0", tk.END)
        self.result_text.tag_remove("current_search", "1.0", tk.END)

        for i, (start, end) in enumerate(self.search_matches):
            # 检查此匹配是否可见（未被折叠）
            start_line = int(start.split('.')[0])
            is_visible = True

            # 检查是否有任何祖先行被折叠
            for line_num in self.folded_lines:
                if self.folded_lines[line_num] and line_num < start_line:
                    line_level = self.line_indent_levels[line_num - 1]
                    start_level = self.line_indent_levels[start_line - 1]
                    if start_level > line_level:
                        is_visible = False
                        break

            if is_visible:
                self.result_text.tag_add("search", start, end)
                if i == self.current_match:
                    self.result_text.tag_add("current_search", start, end)

    def open_and_parse(self):
        """打开并解析 lib 文件"""
        file_path = filedialog.askopenfilename(
            title="选择 lib 文件",
            filetypes=[("Lib 文件", "*.lib *.a"), ("所有文件", "*.*")]
        )

        if file_path:
            self.current_file = file_path
            self.file_path_var.set(file_path)
            self.parse_lib_file()

            # 启用相关按钮
            self.reparse_btn.config(state=NORMAL)
            self.save_btn.config(state=NORMAL)
            self.close_btn.config(state=NORMAL)
            # self.fold_all_btn.config(state=NORMAL)
            # self.unfold_all_btn.config(state=NORMAL)

    def reparse(self):
        """重新解析当前文件"""
        if self.current_file and os.path.exists(self.current_file):
            self.parse_lib_file()
        else:
            messagebox.showerror("错误", "当前文件不存在或已被删除")
            self.close_file()

    def parse_lib_file(self):
        """解析 lib 文件并显示结果"""
        try:
            self.status_var.set(f"正在解析 {os.path.basename(self.current_file)}...")
            self.update()

            self.parse_results = []
            self.line_indent_levels = []  # 重置缩进级别记录
            self.folded_lines = {}  # 重置折叠状态
            self.clear_search()  # 清除搜索状态

            with open(self.current_file, 'rb') as f:
                try:
                    # 直接尝试解析为 ELF 文件
                    elf = ELFFile(f)
                    self.parse_results.append(f"文件: {self.current_file}")
                    self.line_indent_levels.append(0)
                    self.parse_results.append(f"格式: ELF 二进制文件")
                    self.line_indent_levels.append(0)
                    self.parse_results.append("")
                    self.line_indent_levels.append(0)
                    self.parse_elf_file(elf, 0)  # 层级 0，无缩进

                except Exception as e:
                    # 尝试解析为静态库中的多个ELF目标文件
                    self.parse_results.append(f"文件: {self.current_file}")
                    self.line_indent_levels.append(0)
                    self.parse_results.append(f"格式: 静态库 (尝试解析内部目标文件)")
                    self.line_indent_levels.append(0)
                    self.parse_results.append("")
                    self.line_indent_levels.append(0)

                    # 简单检测是否为AR格式（前8字节为"!<arch>\n"）
                    f.seek(0)
                    header = f.read(8)
                    if header == b'!<arch>\n':
                        self.parse_results.append("检测到 AR 归档格式，解析内部目标文件:")
                        self.line_indent_levels.append(0)
                        self.parse_ar_like_file(f)
                    else:
                        self.parse_results.append(f"无法解析文件格式: {str(e)}")
                        self.line_indent_levels.append(0)

            # 显示解析结果（考虑折叠）
            self.update_display_with_folding()
            self.status_var.set(f"解析完成，共 {len(self.parse_results)} 行结果")

        except Exception as e:
            self.status_var.set("解析失败")
            messagebox.showerror("解析错误", f"解析文件时发生错误:\n{str(e)}")
            print(traceback.format_exc())

    def parse_ar_like_file(self, f):
        """解析AR格式文件"""
        try:
            # 跳过AR文件头
            f.seek(8)
            entry_count = 0

            while True:
                # 读取AR条目头（60字节）
                entry_header = f.read(60)
                if not entry_header or len(entry_header) < 60:
                    break

                # 解析条目信息
                entry_name = entry_header[0:16].decode().strip()
                entry_size = int(entry_header[48:58].strip())

                if entry_name and entry_size > 0:
                    entry_count += 1
                    self.parse_results.append(f"目标文件 {entry_count}: {entry_name}")
                    self.line_indent_levels.append(0)

                    # 尝试解析条目中的ELF文件
                    try:
                        # 保存当前位置并读取条目内容
                        current_pos = f.tell()
                        elf_data = f.read(entry_size)

                        # 使用BytesIO创建类文件对象
                        elf = ELFFile(BytesIO(elf_data))
                        self.parse_elf_file(elf, 1)  # 层级1，缩进显示

                    except Exception as e:
                        self.parse_results.append(f"  无法解析为ELF文件: {str(e)}")
                        self.line_indent_levels.append(1)

                    self.parse_results.append("")
                    self.line_indent_levels.append(0)

                    # 移动到下一个条目（AR条目大小按偶数对齐）
                    next_pos = current_pos + entry_size
                    if next_pos % 2 != 0:
                        next_pos += 1
                    f.seek(next_pos)

            if entry_count == 0:
                self.parse_results.append("  未找到可解析的目标文件")
                self.line_indent_levels.append(1)

        except Exception as e:
            self.parse_results.append(f"  解析AR文件时出错: {str(e)}")
            self.line_indent_levels.append(1)

    def parse_elf_file(self, elf, base_indent_level):
        """解析ELF文件内容，带层级缩进"""
        # 文件头信息
        self.parse_results.append(f"{'  ' * base_indent_level}ELF信息:")
        self.line_indent_levels.append(base_indent_level)

        info_level = base_indent_level
        self.parse_results.append(f"{'  ' * info_level}类型: {elf.header['e_type']}")
        self.line_indent_levels.append(info_level)
        self.parse_results.append(f"{'  ' * info_level}机器架构: {elf.header['e_machine']}")
        self.line_indent_levels.append(info_level)
        self.parse_results.append(f"{'  ' * info_level}版本: {elf.header['e_version']}")
        self.line_indent_levels.append(info_level)
        self.parse_results.append(f"{'  ' * info_level}入口点: 0x{elf.header['e_entry']:x}")
        self.line_indent_levels.append(info_level)
        self.parse_results.append(f"{'  ' * info_level}节区数量: {elf.header['e_shnum']}")
        self.line_indent_levels.append(info_level)
        self.parse_results.append(f"{'  ' * info_level}程序头数量: {elf.header['e_phnum']}")
        self.line_indent_levels.append(info_level)

        # 节区信息
        sections = list(elf.iter_sections())
        section_level = base_indent_level
        self.parse_results.append(f"{'  ' * section_level}节区信息 (共 {len(sections)} 个):")
        self.line_indent_levels.append(section_level)

        # 决定显示多少个节区
        display_count = len(sections) if self.full_parse else min(10, len(sections))
        for i, section in enumerate(sections[:display_count]):
            section_info_level = base_indent_level + 1
            self.parse_results.append(f"{'  ' * section_info_level}节区 {i + 1}: {section.name}")
            self.line_indent_levels.append(section_info_level)
            self.parse_results.append(f"{'  ' * (section_info_level + 1)}类型: {section['sh_type']}")
            self.line_indent_levels.append(section_info_level + 1)
            self.parse_results.append(f"{'  ' * (section_info_level + 1)}地址: 0x{section['sh_addr']:x}")
            self.line_indent_levels.append(section_info_level + 1)
            self.parse_results.append(f"{'  ' * (section_info_level + 1)}大小: {section['sh_size']} 字节")
            self.line_indent_levels.append(section_info_level + 1)

        if not self.full_parse and len(sections) > 10:
            self.parse_results.append(f"{'  ' * (base_indent_level + 1)}... 省略 {len(sections) - 10} 个节区 ...")
            self.line_indent_levels.append(base_indent_level + 1)

        # 符号表
        symtab = elf.get_section_by_name('.symtab')
        if symtab:
            symbols = list(symtab.iter_symbols())
            symbol_level = base_indent_level
            self.parse_results.append(f"{'  ' * symbol_level}符号表 (共 {len(symbols)} 个符号):")
            self.line_indent_levels.append(symbol_level)

            # 决定显示多少个符号
            display_sym_count = len(symbols) if self.full_parse else min(20, len(symbols))
            for i, sym in enumerate(symbols[:display_sym_count]):
                sym_info_level = base_indent_level + 1
                sym_type = sym['st_info']['type']
                sym_name = sym.name if sym.name else "无名符号"

                self.parse_results.append(f"{'  ' * sym_info_level}符号 {i + 1}: {sym_name}")
                self.line_indent_levels.append(sym_info_level)
                self.parse_results.append(
                    f"{'  ' * (sym_info_level + 1)}类型: {sym_type}, 绑定: {sym['st_info']['bind']}")
                self.line_indent_levels.append(sym_info_level + 1)
                self.parse_results.append(
                    f"{'  ' * (sym_info_level + 1)}值: 0x{sym['st_value']:x}, 大小: {sym['st_size']}")
                self.line_indent_levels.append(sym_info_level + 1)

            if not self.full_parse and len(symbols) > 20:
                self.parse_results.append(f"{'  ' * (base_indent_level + 1)}... 省略 {len(symbols) - 20} 个符号 ...")
                self.line_indent_levels.append(base_indent_level + 1)

    def save_results(self):
        """保存解析结果到文件（保存完整内容，不受折叠影响）"""
        if not self.parse_results:
            messagebox.showinfo("提示", "没有可保存的解析结果")
            return

        default_filename = os.path.splitext(os.path.basename(self.current_file))[0] + "_解析结果.txt"
        save_path = filedialog.asksaveasfilename(
            title="保存解析结果",
            defaultextension=".txt",
            initialfile=default_filename,
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )

        if save_path:
            try:
                # 保存完整内容，不考虑折叠状态
                with open(save_path, 'w', encoding='utf-8') as f:
                    f.write("\n".join(self.parse_results))
                self.status_var.set(f"解析结果已保存到 {save_path}")
                messagebox.showinfo("成功", f"解析结果已保存到:\n{save_path}")
            except Exception as e:
                self.status_var.set("保存失败")
                messagebox.showerror("保存错误", f"保存文件时发生错误:\n{str(e)}")

    def close_file(self):
        """关闭当前文件，清空显示"""
        self.current_file = None
        self.file_path_var.set("")
        self.parse_results = []
        self.line_indent_levels = []
        self.folded_lines = {}
        self.clear_search()

        self.result_text.config(state=NORMAL)
        self.result_text.delete(1.0, END)
        self.result_text.config(state=DISABLED)

        self.update_line_numbers()

        # 禁用相关按钮
        self.reparse_btn.config(state=DISABLED)
        self.save_btn.config(state=DISABLED)
        self.close_btn.config(state=DISABLED)
        # self.fold_all_btn.config(state=DISABLED)
        # self.unfold_all_btn.config(state=DISABLED)

        self.status_var.set("就绪")
    def create_widgets(self):
        pass